#include "pkgframe.h"
#include <stdio.h>
#include "com_def.h"
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#ifdef _WIN32
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#endif
#include <time.h>

void demo() {
    int ret;
    struct package_frame frame;
    package_frame_init(&frame, NULL, 1);

    // 添加文件
    ret = package_frame_append(&frame, "a.exe", "", "", "v50", 1, 1);
    if (ret) {
        LOG_ERROR("package_frame_append fail, %d", ret);
    }
    ret = package_frame_append(&frame, "../st_link.zip", "", "note st_link.zip", "v1", 1,1 );
    if (ret) {
        LOG_ERROR("package_frame_append fail, %d", ret);
    }
    ret = package_frame_append(&frame, "sha1.c", "", "test file sha1.c", "v50", 1, 1);
    if (ret) {
        LOG_ERROR("package_frame_append fail, %d", ret);
    }
    ret = package_frame_append(&frame, "111.txt", "", "", "", 0, 1);
    if (ret) {
        LOG_ERROR("package_frame_append fail, %d", ret);
    }
    ret = package_frame_append(&frame, "readme.md", "", "test file readme", "v20", 1, 1);
    if (ret) {
        LOG_ERROR("package_frame_append fail, %d", ret);
    }
    // 生成包
    ret = package_frame_generate(&frame, "output/package.pk");
    LOG_INFO("package_frame_generate: %d", ret);
    package_frame_free(&frame);

    // 读取包
    ret = package_frame_read_package(&frame, "output/package.pk");
    LOG_INFO("package_frame_read_package: %d", ret);

    // 输出特定文件
    ret = package_frame_output_file(&frame, "1.txt", "./");
    LOG_INFO("package_frame_output_file: %d", ret);

    // 输出全部文件
    ret = package_frame_output_all(&frame, "output");
    LOG_INFO("package_frame_output_all: %d", ret);

    package_frame_free(&frame);

    //此函数用于合并不同frame生成的文件，使同一个文件可以存在多个不同秘钥的包
    merge_file("package1.pk", "package2.pk", "package12.pk");
}

void usage(void) {
    printf("pkgframe(%s) usage:\n", package_frame_version_full());
    printf("pkgframe [-p file1 file2 ...] out_file  \"package files\"\n");
    printf("pkgframe -l out_file                    \"show package list\"\n");
    printf("pkgframe -d out_file <file1 file2 ...>  \"decode package and create files\"\n");
    printf("pkgframe [-m file1 file2 ...] out_file  \"merge files to one\"\n");
    printf("pkgframe -k key_file [-<p|l|d> ...]     \"select key file to execute\"\n");
    printf("pkgframe -c key_file                    \"create key file\"\n");
    printf("pkgframe -s option                      \"using encrypt: (default)1 open, 0 no encrypt \"\n");
    printf("pkgframe -t type                        \"set up file types\"\n");
    printf("pkgframe -f filefolder                  \"set up file folder\"\n");
    printf("         -y                             \"accept without tips\"\n");
    printf("         -n                             \"reject without tips\"\n");
    exit(-1);
}

int isOpt(char *str) {
    if (strcmp(str, "-p")==0) return 'p';
    else if (strcmp(str, "-l")==0) return 'l';
    else if (strcmp(str, "-d")==0) return 'd';
    else if (strcmp(str, "-m")==0) return 'm';
    else if (strcmp(str, "-k")==0) return 'k';
    else if (strcmp(str, "-c")==0) return 'c';
    else if (strcmp(str, "-y")==0) return 'y';
    else if (strcmp(str, "-n")==0) return 'n';
    else if (strcmp(str, "-s")==0) return 's';
    else if (strcmp(str, "-t")==0) return 't';
    else if (strcmp(str, "-f")==0) return 'f';
    return 0;
}

int count_bits(int value, int cond) {
    int count = 0;
    value &= cond;
    while (value) {
        count += (value & 1);
        value >>= 1;
    }
    return count;
}

int is_allow_continue(char *filename, int notips) {
    if ((access(filename, F_OK) == 0)) {
        if (notips == -1) {
            printf("fail: file %s is exist", filename);
            return 0;
        }
        else if (!notips) {
            printf("file %s is exist, do you want to cover it? (y or n) \n", filename);
            #ifdef _WIN32
            if(getch() != 'y') return 0;
            #else
            struct termios oldt, newt;
            tcgetattr(STDIN_FILENO, &oldt); // 获取当前终端属性  
            newt = oldt;  
            newt.c_lflag &= ~(ICANON | ECHO); // 关闭规范输入和回显  
            tcsetattr(STDIN_FILENO, TCSANOW, &newt); // 应用新的终端属性

            if(getchar() != 'y') {
                tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // 恢复原始终端属性
                return 0;
            }
            tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // 恢复原始终端属性

            #endif
        }
    }
    return 1;
}

#define FLAG_p 0x1
#define FLAG_l 0x10
#define FLAG_d 0x100
#define FLAG_m 0x1000
#define FLAG_k 0x10000
#define FLAG_c 0x100000

int main(int argc, char **argv) {
    if (argc < 3 || argc >= 255) usage();
    int flag=0;   // pldmkc
    int pfcnt=0, notips=0, ret;
    char pf[255];
    char outfile[256]={0}, keyfile[256]={0}, filefolder[256]={0};
    struct package_frame pkf;
    char key[32], is_encrypt=1, type=1;

    for (int i=1;i<argc;i++) {
        switch (isOpt(argv[i]))
        {
        case 'p':
            i++;
            flag|=FLAG_p;
            while (i<argc && !isOpt(argv[i])) {
                pf[pfcnt++] = i++;
            }
            i--;
            break;
        case 'l':
            flag|=FLAG_l;
            if (++i<argc && !isOpt(argv[i])) {
                strncpy(outfile, argv[i], sizeof(outfile));
                outfile[sizeof(outfile)-1] = 0;
            } else {
                usage();
            }
            break;
        case 'd':
            flag|=FLAG_d;
            if (++i<argc && !isOpt(argv[i])) {
                strncpy(outfile, argv[i], sizeof(outfile));
                outfile[sizeof(outfile)-1] = 0;
            } else {
                usage();
            }
            i++;
            while (i<argc && !isOpt(argv[i])) {
                pf[pfcnt++] = i++;
            }
            i--;
            break;
        case 'm':
            i++;
            flag|=FLAG_m;
            while (i<argc && !isOpt(argv[i])) {
                pf[pfcnt++] = i++;
            }
            i--;
            break;
        case 'k':
            flag|=FLAG_k;
            if (++i<argc && !isOpt(argv[i])) {
                strncpy(keyfile, argv[i], sizeof(keyfile));
                keyfile[sizeof(keyfile)-1] = 0;
            } else {
                usage();
            }
            break;
        case 'c':
            flag|=FLAG_c;
            if (++i<argc && !isOpt(argv[i])) {
                strncpy(keyfile, argv[i], sizeof(keyfile));
                keyfile[sizeof(keyfile)-1] = 0;
            } else {
                usage();
            }
            break;
        case 'y':
            notips = 1;
            break;
        case 'n':
            notips = -1;
            break;
        case 's':
            if (++i<argc && !isOpt(argv[i])) {
                is_encrypt = atoi(argv[i]);
            } else {
                usage();
            }
            break;
        case 't':
            if (++i<argc && !isOpt(argv[i])) {
                type = atoi(argv[i]);
            } else {
                usage();
            }
            break;
        case 'f':
            if (++i<argc && !isOpt(argv[i])) {
                strncpy(filefolder, argv[i], 255);
            } else {
                usage();
            }
            break;
        default:
            usage();
            break;
        }
    }
    if (count_bits(flag, FLAG_p|FLAG_l|FLAG_d|FLAG_m) > 1) usage();
    
    // create key
    if (flag&FLAG_c) {
        srand(time(NULL));
        printf("your key is ");
        for (int i=0;i<32;i++) {
            key[i] = rand();
            printf("%d ",key[i]);
        }
        printf("\n");
        if (is_allow_continue(keyfile, notips)) {
            FILE *fp = fopen(keyfile, "wb");
            if (!fp) {
                perror("create keyfile");
            } else {
                fwrite(key, 1, 32, fp);
                fclose(fp);
            }
        }
    } else if (flag&FLAG_k) {
        FILE *fp = fopen(keyfile, "rb");
        if (!fp) {
            perror("set keyfile");
            return -1;
        } else {
            ret = fread(key, 1, 32, fp);
            fclose(fp);
            if (ret != 32) {
                printf("keyfile is illegal, ret = %d\n", ret);
                return -1;
            }
        }
        package_frame_init(&pkf, key, is_encrypt);
    }
    if (!(flag&FLAG_k)) {
        package_frame_init(&pkf, NULL, is_encrypt);
    }

    if (flag&FLAG_p) {
        if (pfcnt<2) usage();
        strncpy(outfile, argv[pf[pfcnt-1]], sizeof(outfile));
        outfile[sizeof(outfile)-1] = 0;
        pfcnt--;
        char note[256];
        time_t t = time(NULL);
        struct tm *l = localtime(&t);
        sprintf(note, "generate at %04d-%02d-%02d %02d:%02d:%02d", 
            l->tm_year+1900, l->tm_mon+1, l->tm_mday, l->tm_hour, l->tm_min, l->tm_sec);
        for (int i=0;i<pfcnt;i++) {
            ret = package_frame_append(&pkf, argv[pf[i]], filefolder, note, package_frame_version(), type, is_encrypt);
            if (ret) printf("append %s fail: %d\n", argv[pf[i]], ret);
        }
        if (is_allow_continue(outfile, notips)) {
            // LOG_DEBUG("%x %x",&pkf, outfile);
            ret = package_frame_generate(&pkf, outfile);
            printf("generate %d files into %s\n", ret, outfile);
        }
    } else if (flag&FLAG_l) {
        ret = package_frame_read_package(&pkf, outfile);
        struct p_module_unit *u = pkf.unit;
        printf("read %d files in packet\n", ret);
        printf("'%s' list:\n", outfile);
        printf("name | size | filefolder | note | version | type\n");
        while (u) {
            printf("%s | %d | %s | %s | %s | %d\n", u->filename, u->data_len, u->fileFolder, u->note, u->version, u->type);
            u = u->next;
        }
    } else if (flag&FLAG_d) {
        ret = package_frame_read_package(&pkf, outfile);
        if (ret == 0) {
            printf("No file can be analysis\n");
            return 0;
        }
        if (pfcnt) {
            for (int i=0;i<pfcnt;i++) {
                if (is_allow_continue(argv[pf[i]], notips)) {
                    ret = package_frame_output_file(&pkf, argv[pf[i]], ".");
                    if (ret) printf("output file %s fail, ret = %d\n", argv[pf[i]], ret);
                    else printf("output file %s\n", argv[pf[i]]);
                }
            }
        } else {
            struct p_module_unit *u = pkf.unit;
            char filepath[256]={0};
            while (u) {
                if (u->fileFolder[0]) snprintf(filepath,sizeof(filepath)-1,"%s/%s");
                else strcpy(filepath, u->filename);
                if (!is_allow_continue(filepath, notips)) return 0;
                u = u->next;
            }
            ret = package_frame_output_all(&pkf, ".");
            printf("output %d files\n", ret);
        }
    } else if (flag&FLAG_m) {
        if (pfcnt<3) usage();
        strncpy(outfile, argv[pf[pfcnt-1]], sizeof(outfile));
        outfile[sizeof(outfile)-1] = 0;
        pfcnt--;
        if (is_allow_continue(outfile, notips)) {
            merge_file(argv[pf[0]], argv[pf[1]], outfile);
            for (int i=2; i<pfcnt; i++) {
                merge_file(outfile, argv[pf[i]], outfile);
            }
        }
    }

    package_frame_free(&pkf);

    return 0;
}
